﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;

namespace GameFrameWork.Editor
{
    public sealed class Tmp_Proto_netGenerator
    {
        private const string OutputCodePath = "Assets/Game/Scripts/ProtoBuffNet/SCEventArgs";
        private const string CodeTemplateFileName = "Assets/DevelopTool/ScriptGenerator/Editor/Proto-netGenerator/TmpSCEventArgsCodeTemplate.txt";
        static string output = "Assets/Game/Scripts/ProtoBuffNet/Protocol/";

        static string InputDirectory
        {
            get
            {
                string directory = EditorPrefs.GetString("ProtoDirector");
                return directory;
            }
        }

        static string protoTemplete = "Assets/DevelopTool/ScriptGenerator/Editor/Proto-netGenerator/TmpProtoTemplete.txt";
        static string handlerTemplete = "Assets/DevelopTool/ScriptGenerator/Editor/Proto-netGenerator/TmpHandlerTemplete.txt";

        private static List<string> systemTypes = new List<string>() { "int", "long", "float", "bool", "byte", "double" };

        [MenuItem("开发工具库/设置Proto目录", priority = EditorSetting.EditorMenu.GENERATE_GENERATE_PROTO)]
        private static void SetProtoDirectory()
        {
            string directory = EditorUtility.OpenFolderPanel("选择 DataTables 表目录", InputDirectory, string.Empty);
            if (!string.IsNullOrEmpty(directory))
            {
                EditorPrefs.SetString("ProtoDirector", directory + "/");
            }
        }

        [MenuItem("开发工具库/Proto文件转化", priority = EditorSetting.EditorMenu.GENERATE_GENERATE_PROTO - 1)]
        private static void GenerateProtoFile()
        {
            var input = InputDirectory;
            if (input.IsNullOrEmpty())
            {
                throw new Exception("请先配置需要导入的目录");
            }
            List<string> fileNames = new List<string>();
            var dirctory = new DirectoryInfo(input);
            var files = dirctory.GetFiles("*", SearchOption.TopDirectoryOnly);
            for (int i = 0; i < files.Length; i++)
            {
                if (!files[i].Name.EndsWith(".proto"))
                    continue;
                fileNames.Add(files[i].Name);
            }

            Dictionary<string, ProtoMessageStruct> scs = new Dictionary<string, ProtoMessageStruct>();
            List<string> messages = new List<string>();

            if (false)
            {
                // clean.
                var csFiles = Directory.GetFiles(output, "*.cs", SearchOption.AllDirectories);
                foreach (var f in csFiles)
                {
                    if (File.Exists(f))
                        File.Delete(f);
                }
            }

            foreach (var item in fileNames)
            {
                build(item, scs, messages, input);
            }

            // ProtoResigst
            StringBuilder sb = new StringBuilder();
            foreach (var entry in messages)
            {
                sb.AppendFormat("                Register(\"{0}\", typeof({0}));", entry).AppendLine();
            }
            AssetDatabase.Refresh();
        }


        public static void build(string fileNames, Dictionary<string, ProtoMessageStruct> scs, List<string> messages, string input)
        {
            string[] lines = File.ReadAllLines(input + fileNames, Encoding.UTF8);
            int rawRowCount = lines.Length;

            ProtoMessageStruct pms = new ProtoMessageStruct();
            pms.properties = new List<ProtoMessageProperty>();

            pms.MESSAGE_CODE = "";
            Debug.Log("当前处理的文件" + fileNames);
            for (int i = 0; i < lines.Length; i++)
            {
                string line = lines[i].Replace("\n", "").Trim();

                if (line.StartsWith("//"))
                {
                    pms.MESSAGE_CODE = line.Replace("//", "").Trim();
                }
                else if (line.StartsWith("message"))
                {
                    string[] ss = line.Replace("message", "").Split(' ');
                    pms.MESSAGE_CLASS_NAME = ss[1].Trim();
                    pms.MESSAGE_COMMENT = ss.Length >= 5 ? ss[4].Trim() : "";
                }
                else if (line.StartsWith("optional") || line.StartsWith("repeated"))
                {
                    Debug.Log(line);
                    string[] ss = line.Split(';');
                    string[] ss2 = ss[0].Split(' ');
                    string a1 = ss2[0];
                    string type = ss2[1];

                    string name = ss2[2];
                    int num = int.Parse(ss2[4]);
                    string note = ss[1].Replace("//", "").Trim();

                    if ("int32".Equals(type))
                    {
                        type = "int";
                    }
                    else if ("int64".Equals(type))
                    {
                        type = "long";
                    }
                    else if (type.StartsWith("map"))
                    {
                        type = type.Replace("map", "Dictionary");
                        type = type.Replace("int32", "int");
                        type = type.Replace("int64", "long");
                        type = type.Replace(",", ", ");
                    }

                    ProtoMessageProperty pmp = new ProtoMessageProperty()
                    {
                        repreated = a1.Equals("repeated"),
                        type = type,
                        name = name,
                        num = num,
                        note = note
                    };
                    pms.properties.Add(pmp);
                }
                else if (line.StartsWith("}"))
                {
                    GenerateProto(pms);
                    messages.Add(pms.MESSAGE_CLASS_NAME);
                    if (pms.MESSAGE_CODE.Length > 0 && pms.MESSAGE_CLASS_NAME.StartsWith("SC"))
                    {
                        scs.Add(pms.MESSAGE_CODE, pms.Clone());
                        GeneratorHandler(pms);
                        GeneratorEventArgs(pms);
                    }
                    pms.MESSAGE_CODE = "";
                    pms.properties.Clear();
                }
                else if (line.Length == 0)
                {

                }


            }
        }

        private static void GeneratorEventArgs(ProtoMessageStruct pms)
        {
            string codeFileName = OutputCodePath + "/" + pms.MESSAGE_CLASS_NAME + "EventArgs.cs";
            try
            {
                //获取代码模版
                string codeTemplate = File.ReadAllText(CodeTemplateFileName, Encoding.UTF8);
                StringBuilder codeTemplateStringBuilder = new StringBuilder(codeTemplate);

                //代码替换内容
                codeTemplateStringBuilder.Replace("__EVENT_NAME__", pms.MESSAGE_CLASS_NAME);
                codeTemplateStringBuilder.Replace("__COMMNENT_EVENT_NAME__", pms.MESSAGE_COMMENT);

                //生成代码文件
                using (FileStream codeFileStream = new FileStream(codeFileName, FileMode.Create))
                {
                    using (StreamWriter codeStreamWriter = new StreamWriter(codeFileStream, Encoding.UTF8))
                    {
                        codeStreamWriter.Write(codeTemplateStringBuilder.ToString());
                    }
                }
            }
            catch (Exception exception)
            {
                Debug.LogError(string.Format("Generate code file '{0}' failure, exception is '{1}'.", codeFileName, exception.Message));
            }
        }

        public static StringBuilder getProtoTemplete()
        {
            string[] lines = File.ReadAllLines(protoTemplete, Encoding.UTF8);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < lines.Length; i++)
            {
                sb.AppendLine(lines[i]);
            }
            // Debug.Log(sb.ToString());
            return sb;
        }

        private static StringBuilder getHandlerTemplete()
        {
            string[] lines = File.ReadAllLines(handlerTemplete, Encoding.UTF8);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < lines.Length; i++)
            {
                sb.AppendLine(lines[i]);
            }
            // Debug.Log(sb.ToString());
            return sb;
        }

        private static string IsHandlerWithMask(string className)
        {
            bool withMask = true;
            if (className.Contains("Heartbeat")
                || className.Contains("SCPush")
                || className.Contains("SCChatBroadcast")
                || className.Contains("SCDictSave"))
            {
                withMask = false;
            }
            return withMask ? "true" : "false";
        }

        private static void GeneratorHandler(ProtoMessageStruct pms)
        {
            StringBuilder codeContent = getHandlerTemplete();
            Debug.LogFormat("自动生成Handler {0}Handler", pms.MESSAGE_CLASS_NAME);
            codeContent.Replace("__MESSAGE_CREATE_TIME__", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
            codeContent.Replace("__MESSAGE_CLASS_NAME__", "" + pms.MESSAGE_CLASS_NAME);
            codeContent.Replace("__MESSAGE_CODE__", pms.MESSAGE_CODE);
            codeContent.Replace("__MESSAGE_WITHMASK__", IsHandlerWithMask(pms.MESSAGE_CLASS_NAME));

            using (FileStream fileStream = new FileStream(output + pms.MESSAGE_CLASS_NAME + "Handler.cs", FileMode.Create))
            {
                using (StreamWriter stream = new StreamWriter(fileStream, Encoding.UTF8))
                {
                    stream.Write(codeContent.ToString());
                }
            }

        }

        public static void GenerateProto(ProtoMessageStruct pms)
        {
            StringBuilder codeContent = getProtoTemplete();
            Debug.LogFormat("自动生成Proto {0}", pms.MESSAGE_CLASS_NAME);

            if (pms.MESSAGE_CODE == null || pms.MESSAGE_CODE.Length == 0)
            {
                codeContent.Replace(@"__MESSAGE_ID__", "");
                codeContent.Replace(@"__MESSAGE_CLEAR__", "");
                codeContent.Replace(@"__MESSAGE_RELEASE__", "");
            }
            else
            {
                StringBuilder a = new StringBuilder();
                a.AppendLine()
                .AppendLine("    public const int Code = " + pms.MESSAGE_CODE + ";")
                .AppendLine()
                .AppendLine("    public int Id")
                .AppendLine("    {")
                .AppendLine("        get")
                .AppendLine("        {")
                .AppendFormat("            return {0};", pms.MESSAGE_CODE).AppendLine()
                .AppendLine("        }")
                .AppendLine("    }");
                codeContent.Replace("__MESSAGE_ID__", a.ToString());
            }

            codeContent.Replace("__MESSAGE_CREATE_TIME__", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
            codeContent.Replace("__MESSAGE_CLASS_NAME__", "" + pms.MESSAGE_CLASS_NAME);
            codeContent.Replace("__MESSAGE_COMMENT__", pms.MESSAGE_COMMENT);
            codeContent.Replace("__MESSAGE_SUPER_CLASS__", pms.MESSAGE_CODE == null || pms.MESSAGE_CODE.Length == 0 ? " : Helper.IProtoBase" : (pms.MESSAGE_CLASS_NAME.StartsWith("CS") ? " : Helper.IProtoBase, Helper.ICSProtoMessage" : " : Helper.IProtoBase, Helper.ISCProtoMessage"));
            codeContent.Replace("__MESSAGE_PROPERTIES__", GenerateDataTableProperties(pms.properties));
            codeContent.Replace("__MESSAGE_CLEAR__", GenerateDateTableClear(pms.properties));
            codeContent.Replace("__MESSAGE_RELEASE__", GenerateDateTableRelease(pms.properties));

            // Debug.Log(codeContent.ToString());
            using (FileStream fileStream = new FileStream(output + pms.MESSAGE_CLASS_NAME + ".cs", FileMode.Create))
            {
                using (StreamWriter stream = new StreamWriter(fileStream, Encoding.UTF8))
                {
                    stream.Write(codeContent.ToString());
                }
            }

        }

        private static string GenerateDataTableProperties(List<ProtoMessageProperty> properties)
        {
            StringBuilder stringBuilder = new StringBuilder();

            foreach (ProtoMessageProperty pmp in properties)
            {
                stringBuilder.AppendLine()
                    .AppendLine("    /// <summary>")
                    .AppendFormat("    /// {0}。", pmp.note).AppendLine()
                    .AppendLine("    /// </summary>");
                if (pmp.repreated)
                    //stringBuilder.AppendFormat("    [ProtoBuf.ProtoMember({0})]", pmp.num).AppendLine()
                    stringBuilder.AppendFormat("    [ProtoBuf.ProtoMember({0}, TypeName = \"{1}\")]", pmp.num, pmp.type).AppendLine()
                    .Append("    public List<" + pmp.type + "> " + pmp.name + " { get; set; }")
                    ;
                else
                    stringBuilder.AppendFormat("    [ProtoBuf.ProtoMember({0})]", pmp.num).AppendLine()
                    .Append("    public " + pmp.type + " " + pmp.name + " { get; set; }")
                    ;
            }
            return stringBuilder.ToString();
        }

        // 读取属性配置, 写清除事件
        private static string GenerateDateTableClear(List<ProtoMessageProperty> properties)
        {
            StringBuilder stringBuilder = new StringBuilder();

            stringBuilder.AppendLine("    public void Clear()").AppendLine("    {");

            foreach (ProtoMessageProperty pmp in properties)
            {
                if (systemTypes.IndexOf(pmp.type) == -1 || pmp.repreated)
                {
                    stringBuilder.AppendFormat("        {0} = null;", pmp.name).AppendLine();
                }
            }
            stringBuilder.AppendLine("    }");
            return stringBuilder.ToString();
        }

        // 读取属性配置, 写释放事件
        private static string GenerateDateTableRelease(List<ProtoMessageProperty> properties)
        {
            StringBuilder stringBuilder = new StringBuilder();

            stringBuilder.AppendLine("    public void Release()").AppendLine("    {");
            stringBuilder.AppendFormat("        ReferencePool.Release(this);").AppendLine();

            stringBuilder.AppendLine("    }");
            return stringBuilder.ToString();
        }
    }
}

